﻿using System;
using System.Data;
using System.Data.Common;
using System.Threading;

//自定义命名空间
using DreamCube.Foundation.Basic.Enums;
using DreamCube.Foundation.Basic.Extensions.Data;
using DreamCube.Foundation.Basic.Extensions.System;
using DreamCube.Framework.DataAccess.Basic;
using DreamCube.Framework.DataAccess.Sqlserver;

namespace DreamCube.DC_52Icon.BLL
{
    /// <summary>
    /// 网站访问量类
    /// </summary>
    public static class AccessCount
    {
        #region "字段"

        /// <summary>
        /// 网站总访问量
        /// </summary>
        private static Int32 totalAccessCount = -1;

        /// <summary>
        /// 网站今日访问量
        /// </summary>
        private static Int32 todayAccessCount = -1;

        /// <summary>
        /// 记录今日访问量的日期，缓存到内存中，利于网站访问速度
        /// </summary>
        private static DateTime todayAccessCount_Date = default(DateTime);

        /// <summary>
        /// 读写锁---今天的访问量
        /// </summary>
        private static ReaderWriterLockSlim todayAccessLocker = new ReaderWriterLockSlim();

        /// <summary>
        /// 读写锁---全局访问的锁
        /// </summary>
        private static ReaderWriterLockSlim totalAccessLocker = new ReaderWriterLockSlim();

        /// <summary>
        /// 超时时间
        /// </summary>
        private static Int32 timeOut = 500;

        /// <summary>
        /// 标志从数据库中读取今天的访问量成功
        /// </summary>
        private static Boolean readTodayAccessCountSuccess = false;

        #endregion

        /// <summary>
        /// 获取总的访问量
        /// </summary>
        /// <returns></returns>
        public static Int32 GetTotalAccessCount()
        {
            if (AccessCount.totalAccessCount != -1)
                return AccessCount.totalAccessCount;
            else
            {
                AccessCount.InitialTotalAccessCount();
                ///初始化完毕当前的总访问量之后，还应该马上递增1
                AccessCount.IncrementTotalAccessCount();
            }

            return AccessCount.totalAccessCount == -1 ? 0 : AccessCount.totalAccessCount;
        }

        /// <summary>
        /// 获取今天的访问量
        /// </summary>
        /// <returns></returns>
        public static Int32 GetTodayAccessCount()
        {
            DateTime now = System.DateTime.Now;
            if (now.EqualsEx(todayAccessCount_Date) && todayAccessCount != -1)
                return AccessCount.todayAccessCount;
            else
            {
                AccessCount.readTodayAccessCountSuccess = false;
                AccessCount.InitialTodayAccessCount();
                ///初始化完毕当前的当天访问量之后，还应该马上递增1
                AccessCount.IncrementTodayAccessCount();
            }
            return AccessCount.todayAccessCount;
        }

        /// <summary>
        /// 增加今天的访问量；
        /// 客户端调用逻辑：理论上应该是：先调用增加访问量的方法，再调用获取访问量的方法
        /// </summary>
        public static void IncrementTodayAccessCount()
        {
            DateTime now = System.DateTime.Now;
            //如果当前访问的时间还属于今天（而非跳过12点）；当前的访问的数量直接加1即可了
            //如果总访问量为-1，证明还没初始化今天的访问量；则马上初始化今天的访问量
            if (!(now.EqualsEx(todayAccessCount_Date) && todayAccessCount != -1))
            {
                //设置未初始化的标志
                AccessCount.readTodayAccessCountSuccess = false;
                AccessCount.InitialTodayAccessCount();
            }

            //增加今天的访问量，注意多线程访问问题（此变量是保存在内存中的，速度很快）
            Interlocked.Increment(ref AccessCount.todayAccessCount);
            //把数据更新到数据库中，由于允许丢失此部分数据，所以可以不用同步更新数据库的
            ThreadPool.QueueUserWorkItem((obj) => { DoUpdateTodayAccessCountToDB(); });
        }

        /// <summary>
        /// 增加总的访问量；此数据只会在内存中存在，不需要与数据库进行什么同步操作
        /// </summary>
        public static void IncrementTotalAccessCount()
        {
            if (totalAccessCount == -1)
                AccessCount.InitialTotalAccessCount();
            //增加今天的访问量，注意多线程访问问题（此变量是保存在内存中的，速度很快）
            Interlocked.Increment(ref AccessCount.totalAccessCount);
        }

        #region "私有方法"

        private static Boolean AddTodayAccessCountRecord()
        {
            String sql = "insert into ACount(ADay,CountSum) values(@ADay,@CountSum)";
            Database db = DbManager.GetDBByName();
            DbCommand command = db.GetSqlStringCommand(sql);
            db.AddInParameter(command, "@ADay", DbType.Date, DateTime.Now.Date);
            db.AddInParameter(command, "@CountSum", DbType.Int32, 1);
            return db.ExecuteNonQuery(command) > 0;
        }

        /// <summary>
        /// 获取今天访问的数量记录
        /// </summary>
        /// <returns></returns>
        private static Model.ACount GetTodayAccessCountRecord()
        {
            String sql = "select * from ACount where ADay = @ADay";
            Database db = DbManager.GetDBByName();
            DbCommand command = db.GetSqlStringCommand(sql);
            db.AddInParameter(command, "@ADay", DbType.Date, System.DateTime.Now.FormatToDay());
            DataTable data = db.ExecuteDataTable(command);
            if (data != null && data.Rows.Count > 0)
            {
                Model.ACount acountRecord = new Model.ACount();
                DataRow rowRecord = data.Rows[0];
                rowRecord.LoadColumnDataToObjProperty<Model.ACount>(acountRecord);
                return acountRecord;
            }
            return null;
        }

        /// <summary>
        /// 初始化今天的访问量；今天的访问量涉及问题比较多；
        /// 此方法只是把当前数据库记录的访问量设置到字段中，调用方应该根据需求，再递增1并返回
        /// </summary>
        private static void InitialTodayAccessCount()
        {
            AccessCount.todayAccessCount = -1;  //重新初始化今天的访问量
            ///双锁模型，获取今天的访问量，如果获取不到记录，则马上添加一条今天的记录；
            ///所以要采用加锁技术，否则可能会导致添加同一天的记录多条
            if (!AccessCount.readTodayAccessCountSuccess)
                if (todayAccessLocker.TryEnterWriteLock(AccessCount.timeOut))
                    if (!AccessCount.readTodayAccessCountSuccess)
                    {
                        try
                        {
                            Model.ACount acount = GetTodayAccessCountRecord();
                            if (acount != null)
                                AccessCount.todayAccessCount = acount.CountSum;
                            else if (AddTodayAccessCountRecord())
                                AccessCount.todayAccessCount = 0;

                            //设置全局缓存访问日期
                            AccessCount.todayAccessCount_Date = System.DateTime.Now.Date;
                            AccessCount.readTodayAccessCountSuccess = true; //设置标志值，表示已经成功获取今天的访问量了
                        }
                        finally
                        {
                            todayAccessLocker.ExitWriteLock();
                        }
                    }
        }

        /// <summary>
        /// 初始化网站的总访问量
        /// </summary>
        /// <returns></returns>
        private static void InitialTotalAccessCount()
        {
            if (totalAccessLocker.TryEnterWriteLock(AccessCount.timeOut))
                if (AccessCount.totalAccessCount == -1)
                {
                    String sql = "select sum(CountSum) from ACount";
                    Database db = DbManager.GetDBByName();
                    DbCommand command = db.GetSqlStringCommand(sql);
                    Object result = db.ExecuteScalar(command);
                    if (result != null)
                        AccessCount.totalAccessCount =
                            result.TryToInt32(OperType.StillReturnWithoutLogWhenFail, -1);
                }
        }

        /// <summary>
        /// 更新今天访问量到数据库中；不一定每次都是+1的
        /// </summary>
        /// <param name="count"></param>
        private static void DoUpdateTodayAccessCountToDB()
        {
            Database db = DbManager.GetDBByName();
            DbCommand command = db.GetStoredProcCommand("UpdateAcount");
            db.AddInParameter(command, "@ADay", DbType.Date, todayAccessCount_Date);
            db.AddInParameter(command, "@countSum", DbType.Int32,todayAccessCount);
            db.ExecuteNonQuery(command);
        }

        #endregion
    }
}
